package io.kiki.sba.registry.server.data_store.resource;

import io.kiki.sba.registry.common.model.GenericResponse;
import io.kiki.sba.registry.common.model.Tuple;
import io.kiki.sba.registry.common.model.slot.*;
import io.kiki.sba.registry.server.data_store.bootstrap.ServerConfig;
import io.kiki.sba.registry.server.data_store.slot.SlotAccessorDelegate;
import io.kiki.sba.registry.server.shared.meta.MetaServerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.List;


@Path("openapi/v1/slot/table")
public class SlotsStatusResource {
    // TODO this should be config in serverConfig
    public static final long MAX_SYNC_GAP = Long.getLong("registry.data.replicate.max.gap.millis", 3 * 60 * 1000);

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    ServerConfig serverConfig;

    @Autowired
    SlotAccessorDelegate slotAccessorDelegate;

    @Autowired
    MetaServerService metaServerService;

    @GET
    @Path("/sync/task/status")
    @Produces(MediaType.APPLICATION_JSON)
    public GenericResponse<Object> getSlotsSyncTaskStatus() {
        return getSlotsStatus(serverConfig.getDataCenter());
    }

    @GET
    @Path("/sync/task/status/remote")
    @Produces(MediaType.APPLICATION_JSON)
    public GenericResponse<Object> getRemoteSlotsStatus(@QueryParam("dataCenter") String dataCenter) {
        return getSlotsStatus(dataCenter);
    }

    private GenericResponse<Object> getSlotsStatus(String dataCenter) {
        Tuple<Long, List<BaseSlotStatus>> tuple = slotAccessorDelegate.getslotsEpochAndStatuses(dataCenter);
        final long epoch = tuple.o1;
        final List<BaseSlotStatus> slotStatuses = tuple.o2;

        boolean isCurrentSlotStable = true;
        for (BaseSlotStatus slotStatus : slotStatuses) {
            if (slotStatus.getSlotRole() == SlotRole.leader) {
                if (!((LeaderSlotStatus) slotStatus).getLeaderStatus().isHealthy()) {
                    isCurrentSlotStable = false;
                    break;
                }
            } else {
                if (System.currentTimeMillis() - ((FollowerSlotStatus) slotStatus).getLastLeaderSyncTime() > MAX_SYNC_GAP) {
                    isCurrentSlotStable = false;
                    break;
                }
            }
        }
        return new GenericResponse<>().fillSucceed(new SlotsSyncTaskStatus(epoch, isCurrentSlotStable, slotStatuses));
    }

    @GET
    @Path("/status")
    @Produces(MediaType.APPLICATION_JSON)
    public GenericResponse<Object> getSlotsStatus() {
        logger.info("[getSlotsStatus] begin");
        try {
            SlotsStatusResponse SlotsStatus = metaServerService.getSlotsStatus();
            return new GenericResponse<>().fillSucceed(SlotsStatus);
        } catch (Throwable th) {
            logger.error("[getSlotsStatus]", th);
            return new GenericResponse<>().fillFailed(th.getMessage());
        } finally {
            logger.info("[getSlotsStatus] end");
        }
    }

    public static class SlotsSyncTaskStatus {
        private final long epoch;
        private final boolean isCurrentSlotsStable;
        private final List<BaseSlotStatus> slotStatuses;

        public SlotsSyncTaskStatus(long epoch, boolean isCurrentSlotStable, List<BaseSlotStatus> slotStatuses) {
            this.epoch = epoch;
            this.isCurrentSlotsStable = isCurrentSlotStable;
            this.slotStatuses = slotStatuses;
        }

        public long getEpoch() {
            return epoch;
        }

        public boolean isCurrentSlotsStable() {
            return isCurrentSlotsStable;
        }

        public List<BaseSlotStatus> getSlotStatuses() {
            return slotStatuses;
        }
    }
}
